//
// Created by QU on 2023/8/16.
//

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#include "utils.h"





//////////////////////////////////////////////////////////////////////////////////////////////////////


size_t getFrameNumOneLineFromBufferLength(size_t bufferLength, enum VC_TYPE vcType, uint16_t splitedCols) {
    if (bufferLength <= 0 || splitedCols <= 0) {
        printErrorMessage("bufferLength or splitedCols is invalid");
        exit(EXIT_FAILURE);
    }

    // bufferLength 来的是需要承载的数据, 不包含开销, 所以这里是VCX_FRAME_LOAD_LENGTH
    size_t vcTypeLoadLength;
    size_t frameNum;

    switch (vcType) {
        case VC_4:
            vcTypeLoadLength = VC4_FRAME_LOAD_LENGTH;
            break;
        case VC_12:
            vcTypeLoadLength = VC12_FRAME_LOAD_LENGTH;
            break;
        default:
            printErrorMessage("vcType is not supported");
            exit(EXIT_FAILURE);
    }

    frameNum = bufferLength / (splitedCols * vcTypeLoadLength);
    if (bufferLength % (splitedCols * vcTypeLoadLength) != 0) {
        frameNum++;
    }

    return frameNum;
}

size_t getFrameNumOneLineFromOneLineLength(enum VC_TYPE vcType, size_t oneLineLength) {
    if (oneLineLength <= 0) {
        printErrorMessage("oneLineLength is invalid");
        exit(EXIT_FAILURE);
    }

    size_t vcTypeBodyLength;
    switch (vcType) {
        case VC_4:
            vcTypeBodyLength = VC4_FRAME_BODY_LENGTH;
            break;
        case VC_12:
            vcTypeBodyLength = VC12_FRAME_BODY_LENGTH;
            break;
        default:
            printErrorMessage("vcType is not supported");
            exit(EXIT_FAILURE);
    }

    // 一行减去头, 剩下的空间是多个BODY
    size_t bodysLength = oneLineLength - VC_FRAME_HEADER_LENGTH;
    if (bodysLength < vcTypeBodyLength) {
        printErrorMessage("oneLineLength is invalid");
        exit(EXIT_FAILURE);
    }

    if (bodysLength % vcTypeBodyLength == 0) {
        return bodysLength / vcTypeBodyLength;
    } else {
        return (bodysLength / vcTypeBodyLength) + 1;
    }

}

size_t getTotalFrameNumFromBufferLength(size_t bufferLength, enum VC_TYPE vcType, uint16_t splitedCols) {
    return getFrameNumOneLineFromBufferLength(bufferLength, vcType, splitedCols) * splitedCols;
}

size_t getTotalFullLengthOneLineFromBufferLength(size_t bufferLength, enum VC_TYPE vcType, uint16_t splitedCols) {
    if (bufferLength == 0 || splitedCols == 0) {
        return 0;
    }

    size_t vcTypeBodyLength;

    switch (vcType) {
        case VC_4:
            vcTypeBodyLength = VC4_FRAME_BODY_LENGTH;
            break;
        case VC_12:
            vcTypeBodyLength = VC12_FRAME_BODY_LENGTH;
            break;
        default:
            printErrorMessage("vcType is not supported");
            exit(EXIT_FAILURE);
    }
    return (getFrameNumOneLineFromBufferLength(bufferLength, vcType, splitedCols) * vcTypeBodyLength) +
           VC_FRAME_HEADER_LENGTH;
}


size_t getTotalFullLengthFormBufferLength(size_t bufferLength, enum VC_TYPE vcType, uint16_t splitedCols) {
    return getTotalFullLengthOneLineFromBufferLength(bufferLength, vcType, splitedCols) * splitedCols;
}


size_t getBufferLengthFromGenFrameNum(size_t genTotalFrameNum, enum VC_TYPE vcType) {
    // genFrameNum should be TIMES of split_channel number

    size_t loadLength;

    switch (vcType) {
        case VC_4:
            loadLength = VC4_FRAME_LOAD_LENGTH;
            break;

        case VC_12:
            loadLength = VC12_FRAME_LOAD_LENGTH;
            break;
        default:
            printErrorMessage("vcType is not supported");
            exit(EXIT_FAILURE);
    }

    return genTotalFrameNum * loadLength;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////






////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void printErrorMessage(const char *errorMessage) {
#ifdef DEBUG_PRINT_ERROR_MESSAGE
    printf("\033[1;41m[ERROR] %s\033[0m\n", errorMessage);
#endif
}

void printErrorMessageWithExtraMessage(const char *error, const char *errorMessage) {
#ifdef DEBUG_PRINT_ERROR_MESSAGE
    printf("\033[1;41m[ERROR] %s:\033[0m %s\n", error, errorMessage);
#endif
}

void printWarningMessage(const char *warningMessage) {
#ifdef DEBUG_PRINT_WARNING_MESSAGE
    printf("\033[1;35m[WARNING] %s\033[0m\n", warningMessage);
#endif
}

void printWarningMessageWithExtraMessage(const char *warning, const char *warningMessage) {
#ifdef DEBUG_PRINT_WARNING_MESSAGE
    printf("\033[1;35m[WARNING] %s:\033[0m %s\n", warning, warningMessage);
#endif
}

void printInfoMessage(const char *infoMessage) {
#ifdef DEBUG_PRINT_INFO_MESSAGE
    printf("\033[32m[INFO]\033[0m %s\n", infoMessage);
#endif
}

void printInfoMessageWithExtraMessage(const char *info, const char *infoMessage) {
#ifdef DEBUG_PRINT_INFO_MESSAGE
    printf("\033[32m[INFO] %s:\033[0m %s\n", info, infoMessage);
#endif
}

void printDebugMessage(const char *debugMessage) {
#ifdef DEBUG_PRINT_DEBUG_MESSAGE
    printf("\033[36m[DEBUG]\033[0m %s\n", debugMessage);
#endif
}

void printDebugMessageWithExtraMessage(const char *debug, const char *debugMessage) {
#ifdef DEBUG_PRINT_DEBUG_MESSAGE
    printf("\033[36m[DEBUG] %s:\033[0m %s\n", debug, debugMessage);
#endif
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

enum VC_TYPE vcTypeConvertor(uint16_t vcTypeInt) {
    switch (vcTypeInt) {
        case 4:
            return VC_4;
        case 12:
            return VC_12;
        default:
            printErrorMessage("VC type is not supported");
            exit(EXIT_FAILURE);
    }
}

void printVCTypeHeader(const vcTypeHeader_t *header) {
    printf("-------- VC Type Header --------\n");
    for (int i = 0; i < sizeof(vcTypeHeader_t); ++i) {
        printf("%02X ", ((uint8_t *) header)[i]);
    }
    printf("\n");
    printf("logicalChannelID: %hu\n", header->logicalChannelID);
    printf("physicalChannelID: %hu\n", header->physicalChannelID);
    printf("SQ: %u\n", header->SQ);
    printf("frameLength: %hu\n", header->frameLength);
    printf("frameNum: %hu\n", header->frameNum);

    switch (header->vc_type) {
        case VC_4:
            printf("VCType: %s\n", "VC_4");
            break;
        case VC_12:
            printf("VCType: %s\n", "VC_12");
            break;
        default:
            printWarningMessage("VCType: Unknown vcType");
            break;
    }
    printf("--------------------------------\n");
}

void printBufferDataContent(uint8_t *mallocBuffer, size_t bufferSize) {
#ifdef DEBUG_PRINT_BUFFER_CONTENT
    for (size_t i = 0; i < bufferSize; i++) {
        printf("%02x ", (uint8_t) mallocBuffer[i]);
    }
    printf("\n");
#endif
}

void printBufferDataContentLast20Bytes(uint8_t *mallocBuffer, size_t bufferSize) {
#ifdef DEBUG_PRINT_BUFFER_CONTENT
    for (size_t i = bufferSize - 20; i < bufferSize; i++) {
        printf("%02x ", (uint8_t) mallocBuffer[i]);
    }
    printf("\n");
#endif
}

void printOutputBufferContent(enum VC_TYPE vcType, uint16_t splitedCols, size_t bufferOneLineLength,
                              uint8_t (*outBuffer)[bufferOneLineLength]) {
    if (splitedCols < 1) {
        printErrorMessage("splitedCols is too small");
        exit(EXIT_FAILURE);
    }

    if (bufferOneLineLength < VC_FRAME_HEADER_LENGTH) {
        printErrorMessage("bufferOneLineLength too small");
        exit(EXIT_FAILURE);
    }
    vcTypeHeader_t tempHeader;

    size_t vcBodyLength;
    switch (vcType) {
        case VC_4:
            vcBodyLength = VC4_FRAME_BODY_LENGTH;
            break;
        case VC_12:
            vcBodyLength = VC12_FRAME_BODY_LENGTH;
            break;
        default:
            printErrorMessage("VC type is not supported");
            exit(EXIT_FAILURE);
    }

    for (size_t i = 0; i < splitedCols; i++) {
#ifdef DEBUG_PRINT_OUTBUFFER_CONTENT
        printf("outputBuffer[%zu]:\n", i);

        // 打印HEADER部分
#ifdef DEBUG_PRINT_OUTBUFFER_HEADER_CONTENT
        printf("HEADER:\n");
        for (size_t j = 0; j < VC_FRAME_HEADER_LENGTH; ++j) {
            printf("%02X ", (uint8_t) outBuffer[i][j]);
        }
        printf("\n");

        memcpy(&tempHeader, &outBuffer[i][0], sizeof(vcTypeHeader_t));

        printVCTypeHeader(&tempHeader);

#endif
#endif

        // 打印BODY部分, BODY 包含MFI和LOAD
#ifdef  DEBUG_PRINT_OUTBUFFER_BODY_CONTENT
        printf("BODY:\n");
        for (size_t j = VC_FRAME_HEADER_LENGTH; j < bufferOneLineLength; j++) {
            if ((j - VC_FRAME_HEADER_LENGTH) % vcBodyLength == 0 && j != VC_FRAME_HEADER_LENGTH) {
                printf("\n");
            }
            printf("%02X ", (uint8_t) outBuffer[i][j]);
        }
        printf("\n\n");
#endif
    }
}


void testUtils() {

    printErrorMessage("Testing error message");
    printErrorMessageWithExtraMessage("Testing error message with extra message",
                                      "Testing error message with extra message");
    printWarningMessage("Testing warning message");
    printWarningMessageWithExtraMessage("Testing warning message with extra message",
                                        "Testing warning message with extra message");
    printInfoMessage("Testing info message");
    printInfoMessageWithExtraMessage("Testing info message with extra message",
                                     "Testing info message with extra message");
    printDebugMessage("Testing debug message");
    printDebugMessageWithExtraMessage("Testing debug message with extra message",
                                      "Testing debug message with extra message");

    printf("\n");

    // Test getters
    uint16_t defVCType = VC_4;
    uint16_t defSplitCols = 2;

    size_t defBufferLength = 2340 * defSplitCols * 2;
    size_t defOnelineLength = 2340 + 2 + 12;
    size_t defGenFrameNum = 10;

    size_t oneLineFrameNum = getFrameNumOneLineFromBufferLength(defBufferLength, defVCType, defSplitCols);
    size_t oneLineLength = getFrameNumOneLineFromOneLineLength(defVCType, defOnelineLength);
    size_t frameNum = getTotalFrameNumFromBufferLength(defBufferLength, defVCType, defSplitCols);
    size_t totalFullLength = getTotalFullLengthFormBufferLength(defBufferLength, defVCType, defSplitCols);
    size_t oneLineTotalFullLength = getTotalFullLengthOneLineFromBufferLength(defBufferLength, defVCType, defSplitCols);
    size_t bufferLength = getBufferLengthFromGenFrameNum(defGenFrameNum, defVCType);

    printDebugMessageWithExtraMessage(
            "------------------------------------------ Testing getters ------------------------------------------",
            "");

    if (VERBOSE_FLAG) {

        printf("getFrameNumOneLineFromBufferLength: %zu\n", oneLineFrameNum);
        printf("getFrameNumOneLineFromOneLineLength: %zu\n", oneLineLength);
        printf("getTotalFrameNumFromBufferLength: %zu\n", frameNum);
        printf("getTotalFullLengthFormBufferLength: %zu\n", totalFullLength);
        printf("getTotalFullLengthOneLineFromBufferLength: %zu\n", oneLineTotalFullLength);
        printf("getBufferLengthFromGenFrameNum: %zu\n", bufferLength);
    }
    // passed
    printDebugMessageWithExtraMessage(
            "------------------------------------------ Testing getters complete ------------------------------------------",
            "\n");

}



